#pragma once

#include <iostream>
#include <string>
#include "Socket.hpp"
#include "InetAddr.hpp"
#include <memory>
#include <algorithm>
#include <poll.h>

using namespace LogModule;
using std::endl, std::cout, std::string, std::sort;
using std::sort, std::fstream;

using namespace socketModule;
const int gdefaultfd = -1;

#define MAX 4096

class pollServer
{
public:
    pollServer(u_int16_t port) : _port(port),
                                 _listen_socket(std::make_unique<tcpSocket>()),
                                 _isrunning(false)
    {
    }
    ~pollServer()
    {
    }

    void init()
    {
        _listen_socket->bulidTcpSocketMethod(_port);
        for (int i = 0; i < MAX; i++)
        {
            _fds[i].fd = gdefaultfd;
            _fds[i].events = 0;
            _fds[i].revents = 0;
        }

        // 先把唯一的fd添加到poll中
        _fds[0].fd = _listen_socket->fd();
        _fds[0].events |= POLLIN;
    }
    void loop()
    {

        int timeout = 1000;
        _isrunning = true;
        while (_isrunning)
        {

            // * 3. 我可能不能使用accept 来进行阻塞调用。而应该使用select进行就绪事件的监测
            // /此时就是用户告诉内核，你需要帮我关心 &rdfs
            int n = poll(_fds, MAX, timeout);
            switch (n)
            {
            case 0:
                cout << "time out..." << endl;
                break;
            case -1:
                perror("select");
                break;
            default:
                // 有事件就绪
                cout << "有事件就绪" << " timeout: " << timeout << endl;
                Dispatcher();
                break;
            }
            sleep(1);
        }
        _isrunning = false;
    }
    void testFd()
    {
        std::cout << "pollfd" << std::endl;
        for (int i = 0; i < MAX; i++)
        {
            if (_fds[i].fd == gdefaultfd)
            {
                continue;
            }

            std::cout << _fds[i].fd << "[" << events2String(_fds[i].events ) << "]" << std::endl;
        }

        std::cout << "\n";
    }
    std::string events2String(short events)
    {
        std::string s = ((events & POLLIN) ? "pollin" : "");
        s += events & POLLOUT ? "pollout" : "";
        return s;
    }
private:
    void acceptor()
    {
        InetAddr client;
        // _listenSock 就绪，获取新链接
        int newfd = _listen_socket->accepter(&client); // 会不会被阻塞呢
        if (newfd < 0)
            return;
        else
        {
            std::cout << "获取到了一个新链接: " << newfd << " client info: " << client.Addr() << std::endl;
            // recv() ??
            // zdl:: 现在的问题就是你怎么把这个newfd 托管给select
            // zdl:: 让select 来关心上面的事件
            // zdl: 将这个newfd 添加到辅助数组就可完成这样的任务
            int pos = -1;
            for (int j = 0; j < MAX; j++)
            {
                if (_fds[j].fd == gdefaultfd)
                {
                    pos = j;
                    break;
                }
            }
            if (pos == -1)
            {
                // 如果满了可以进行扩容
                LOG(LogLevel::ERROR) << "服务器已经满了。。。";
                close(newfd);
                return;
            }
            else
            {
                _fds[pos].fd = newfd;
                _fds[pos].events = POLLIN;

            }
        }
    }

    void recver(int &who)
    {
        // 合法的，普通的，就绪的 fd
        // zdl:: 虽然这里我们已经读到了这个么多的buffer,
        // 但是还是存在在很多的问题， 还是需要我们对代码进行改进
        char buffer[1024];
        ssize_t n = ::recv(_fds[who].fd, buffer, sizeof(buffer) - 1, 0); // 此时的问题就是在这样的情况下，还会不会阻塞
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client# " << buffer << std::endl;
            // 现在除了打印我们还需将它回显
            std::string message = std::string("echo# ") + buffer;
            send(_fds[who].fd, message.c_str(), message.size(), 0);
        }
        else if (n == 0)
        {
            LOG(LogLevel::DEBUG) << "客户端退出， sockfd: " << _fds[who].fd;
            close(_fds[who].fd);
            _fds[who].fd = gdefaultfd;
            _fds[who].revents = _fds[who].events = 0;
        }
        else
        {
            LOG(LogLevel::DEBUG) << "客户端读取出错， sockfd:" << _fds[who].fd;
            close(_fds[who].fd);
            _fds[who].fd = gdefaultfd;
            _fds[who].revents = _fds[who].events = 0;
        }
    }
    void Dispatcher()
    {
        for (int i = 0; i < MAX; i++) 
        {
            if (_fds[i].fd == gdefaultfd)
            {
                continue;
            }
            // 文件描述符首先必须是合法的
            // 判断 listensock中，是否在 rfds中
            if (_fds[i].fd == _listen_socket->fd())/// 说明该描述符是监听套接字
            {
                if (_fds[i].revents & POLLIN)
                {
                    acceptor();// 连接的获取
                }
            }
            else
            {
                if (_fds[i].fd & POLLIN)
                {
                    recver(i); // * 进行 IO的处理
                }
                // else if(_fds[i].revents & POLLOUT)
                // {
                //     // write
                // }
            }
        }
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socket;
    bool _isrunning;
    struct pollfd _fds[MAX];
};
